package com.winit.openapi.util;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.winit.common.spi.context.CommandContext;
import com.winit.openapi.apiadapter.controller.util.RequestMsg3EUtil;
import com.winit.openapi.constants.ApiConstant;
import com.winit.openapi.model.RequestMsg;

public class SignUtil {

    private static final Logger logger = LoggerFactory.getLogger(SignUtil.class);

    /**
     * 通过RequestMsg构建签名串
     * 
     * @param requestMsg
     * @param token
     * @return
     */
    public static String buildJsonStrByRequestMsg(RequestMsg requestMsg, String token) {
        StringBuffer buf = new StringBuffer();
        buf.append(token);
        buf.append("action").append(requestMsg.getAction());
        buf.append("app_key").append(requestMsg.getApp_key());
        String data = "";
        if (requestMsg.getData() != null && StringUtils.isNotBlank(requestMsg.getData().toString())) {
            data = JSONObject.toJSONString(requestMsg.getData());
        }
        buf.append("data").append(data);
        buf.append("format").append(requestMsg.getFormat() == null ? "" : requestMsg.getFormat());
        buf.append("platform").append(requestMsg.getPlatform() == null ? "" : requestMsg.getPlatform());
        buf.append("sign_method").append(requestMsg.getSign_method() == null ? "" : requestMsg.getSign_method());
        buf.append("timestamp").append(requestMsg.getTimestamp() == null ? "" : requestMsg.getTimestamp());
        buf.append("version").append(requestMsg.getVersion() == null ? "" : requestMsg.getVersion());
        buf.append(token);
        return buf.toString();
    }

    /**
     * 通过客户原始报文构建签名串
     * 
     * @param requestMsg
     * @param token
     * @return
     */
    public static String buildJsonStrBySourceData(RequestMsg requestMsg, String sourceDataStr, String token) {
        StringBuffer buf = new StringBuffer();
        buf.append(token);
        buf.append("action").append(requestMsg.getAction());
        buf.append("app_key").append(requestMsg.getApp_key());
        String data = "";
        if (StringUtils.isNotBlank(sourceDataStr)) {
            data = sourceDataStr;
        }
        buf.append("data").append(data);
        buf.append("format").append(requestMsg.getFormat() == null ? "" : requestMsg.getFormat());
        buf.append("platform").append(requestMsg.getPlatform() == null ? "" : requestMsg.getPlatform());
        buf.append("sign_method").append(requestMsg.getSign_method() == null ? "" : requestMsg.getSign_method());
        buf.append("timestamp").append(requestMsg.getTimestamp() == null ? "" : requestMsg.getTimestamp());
        buf.append("version").append(requestMsg.getVersion() == null ? "" : requestMsg.getVersion());
        buf.append(token);
        return buf.toString();
    }

    /**
     * 使用RequestMsg构建签名串
     * 
     * @param requestMsg
     * @param token
     * @return
     */
    public static String getSignStringByRequestMsg(RequestMsg requestMsg, String token) {
        CommandContext ctx = CommandContext.getContext();
        String uri = ctx.getRequestUrl();
        if (StringUtils.isNotBlank(uri) && uri.contains(ApiConstant.AD_3E_CONTENT_PATH)) {
            return RequestMsg3EUtil.buildJsonStrByRequestMsg(requestMsg, token);
        } else {

            return buildJsonStrByRequestMsg(requestMsg, token);
        }
    }

    /**
     * 使用原始报文构建签名串
     * 
     * @param requestMsg
     * @param dataStr
     * @param token
     * @return
     */
    private static String getSignStringBySourceData(RequestMsg requestMsg, String dataStr, String token) {
        CommandContext ctx = CommandContext.getContext();
        String uri = ctx.getRequestUrl();
        if (StringUtils.isNotBlank(uri) && uri.contains(ApiConstant.AD_3E_CONTENT_PATH)) {
            return RequestMsg3EUtil.buildJsonStrBySourceData(requestMsg, dataStr, token);
        } else {

            return buildJsonStrBySourceData(requestMsg, dataStr, token);
        }
    }

    /**
     * 按字段名字母顺序排列 对参数进行MD5加密取得签名
     * 
     * @param requestMsg
     * @param token
     * @return
     */
    public static String getSign(String jsonStr) {
        return MD5Util.getMd5(jsonStr);
    }

    /**
     * 验证签名
     * 
     * @param requestMsg
     * @param token
     * @param request
     * @return
     */
    public static boolean checkSign(HttpServletRequest request, String token, String signFromUser) {
        RequestMsg requestMsg = RequestUtil.getRequestMsg(request);
        if (requestMsg == null) {
            return false;
        }
        String jsonStr = decode(getSignStringByRequestMsg(requestMsg, token));
        String sign = getSign(jsonStr);
        if (StringUtils.isNotBlank(signFromUser) && StringUtils.isNotBlank(sign) && signFromUser.equals(sign)) {
            return true;
        }
        String sourceInputStr = (String) request.getAttribute(ApiConstant.SOURCE_INPUT_STRING);
        // 记录认证失败的消息
        String sourceEncoding = request.getCharacterEncoding();
        logger.warn("签名失败,用户:" + requestMsg.getApp_key() + "|接口:" + requestMsg.getAction() + "|客户原始编码格式:"
                    + sourceEncoding + "\n客户原始报文:" + sourceInputStr);
        if (APISysConfigUtil.isRecheckSign()) {
            
            return reCheckSign(request, token, signFromUser);
        }
        return false;
    }

    /**
     * 二次验证签名
     * 
     * @param request
     * @param token
     * @param signFromUser
     * @return
     */
    private static boolean reCheckSign(HttpServletRequest request, String token, String signFromUser) {
        RequestMsg requestMsg = RequestUtil.getRequestMsg(request);

        String jsonStr = getSignStringByRequestMsg(requestMsg, token);
        String sign = getSign(jsonStr);
        if (StringUtils.isNotBlank(signFromUser) && StringUtils.isNotBlank(sign) && signFromUser.equals(sign)) {
            logger.info("重新签名成功.no decode...");
            return true;
        }

        String sourceInputStr = (String) request.getAttribute(ApiConstant.SOURCE_INPUT_STRING);
        StringBuffer sourceData = new StringBuffer(sourceInputStr);

        String dataStr = StringUtils.EMPTY;
        try {
            String sourceStr = sourceData.substring(sourceData.indexOf("{") + 1, sourceData.lastIndexOf("}"));
            if (requestMsg.getData() != null && StringUtils.isNotBlank(requestMsg.getData().toString())) {
                if (sourceStr.contains("\"data\":{") || sourceStr.contains("\"data\":")) {
                    dataStr = sourceStr.substring(sourceStr.indexOf("{"), sourceStr.lastIndexOf("}") + 1);
                }
            }
        } catch (Exception e) {
            logger.warn("reCheckSign失败，无效的请求参数：" + sourceInputStr);
        }
        jsonStr = getSignStringBySourceData(requestMsg, dataStr, token);
        sign = getSign(jsonStr);
        if (StringUtils.isNotBlank(signFromUser) && StringUtils.isNotBlank(sign) && signFromUser.equals(sign)) {
            logger.info("重新签名成功.使用原始报文...");
            return true;
        }

        // 记录认证失败的消息
        String sourceEncoding = request.getCharacterEncoding();
        logger.warn("重新签名失败,用户:" + requestMsg.getApp_key() + "|接口:" + requestMsg.getAction() + "|客户原始编码格式:"
                    + sourceEncoding + "\n客户原始报文:" + sourceInputStr);

        return false;

    }

    public static String decode(String unicodeStr) {
        if (unicodeStr == null) {
            return null;
        }
        StringBuffer retBuf = new StringBuffer();
        int maxLoop = unicodeStr.length();
        for (int i = 0; i < maxLoop; i++) {
            if (unicodeStr.charAt(i) == '\\') {
                if ((i < maxLoop - 5) && ((unicodeStr.charAt(i + 1) == 'u') || (unicodeStr.charAt(i + 1) == 'U'))) try {
                    retBuf.append((char) Integer.parseInt(unicodeStr.substring(i + 2, i + 6), 16));
                    i += 5;
                } catch (NumberFormatException localNumberFormatException) {
                    retBuf.append(unicodeStr.charAt(i));
                }
                else retBuf.append(unicodeStr.charAt(i));
            } else {
                retBuf.append(unicodeStr.charAt(i));
            }
        }
        return retBuf.toString();
    }

}
